/*
 * Huawei(R) HiNIC PCI Express Network Controller UEFI Driver.
 * This driver is only usable in the EFI Pre-boot execution environment.
 *
 * Copyright(c) 2020 Huawei Technologies Co., Ltd.
 * All rights reserved.
 *
 */
#include <Uefi.h>
#include <Base.h>
#include <Protocol/DevicePath.h>
#include <Protocol/PciIo.h>
#include <Protocol/SerialIo.h>

#include <Library/BaseLib.h>
#include <Library/UefiLib.h>
#include <Library/DebugLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>

#include "ossl_knl_uefi.h"

#define MAX_DEBUG_MESSAGE_LENGTH 512
#define SECOND_LEVEL_CONVERSION 1000

#ifdef __Taishan__
    /*
     * For Taishan ARM server, we have to raise the TPL to 16
     * prevent tpl assert.(V5 have to raise the tpl to 8)
     */
    #define HINIC3_LOCK_TPL_LEVEL TPL_NOTIFY
#else
    #define HINIC3_LOCK_TPL_LEVEL TPL_CALLBACK
#endif

VOID EFIAPI EfiSerialPortPrint(IN UINTN ErrorLevel, IN CONST CHAR8 *Format,
                               ...)
{
    CHAR8 Buffer[MAX_DEBUG_MESSAGE_LENGTH];
    VA_LIST Marker;
    UINTN Size;
    EFI_STATUS Status;
    EFI_SERIAL_IO_PROTOCOL *SerialIo = NULL;

    if (Format == NULL) {
        return;
    }

    /* Convert the DEBUG() message to an ASCII String */
    VA_START(Marker, Format);
    AsciiVSPrint(Buffer, sizeof(Buffer), Format, Marker);
    VA_END(Marker);

    Status = gBS->LocateProtocol(&gEfiSerialIoProtocolGuid, NULL,
                                 (VOID **)&SerialIo);
    if (EFI_ERROR(Status)) {
        return;
    }

    Size = AsciiStrLen(Buffer);
    if (Size > MAX_DEBUG_MESSAGE_LENGTH - 1) {
        Size = MAX_DEBUG_MESSAGE_LENGTH - 1;
    }

    /* Send the print string to a Serial Port */
    SerialIo->Write(SerialIo, &Size, (VOID *)Buffer);
}

UINT16 ilog2(int n)
{
    UINT16 res = 0;

    while (n > 1) {
        n >>= 1;
        res++;
    }

    return res;
}

/**
 * kzalloc - alloc system memory
 *
 * Alloc normal memory, flags is not used.
 */
void *_kzalloc(size_t size, ulong flags)
{
    if (!size) {
        return NULL;
    }

    return AllocateZeroPool(size);
}

void *kcalloc(size_t n, size_t size, ulong flags)
{
    return _kzalloc(n * size, flags);
}

/**
 * kfree - free system memory
 *
 * Free normal memory.
 */
void _kfree(void *p)
{
    if (p == NULL) {
        return;
    }

    FreePool(p);
    return;
}

void *_vzalloc(size_t size)
{
    if (!size) {
        return NULL;
    }

    return AllocateZeroPool(size);
}

void _vfree(void *p)
{
    if (p == NULL) {
        return;
    }
    FreePool(p);
}

void *get_free_pages(UINT32 order)
{
    return AllocatePages(0x1 << order);
}

void free_pages(UINT64 addr, UINT32 order)
{
    if (!addr) {
        return;
    }

    FreePages((void *)addr, 0x1 << order);
}

static inline unsigned long mktime(EFI_TIME Time)
{
    unsigned long year = Time.Year;
    unsigned long mon = Time.Month;
    unsigned long day = Time.Day;

    /* Puts Feb(2) last since it has leap day */
    if ((INTN)(mon -= 2) <= 0) {
        mon += 12;        /* month:1,2..12 -> 11,12,1..10 */
        year -= 1;
    }

    /*
     * leapyears:Divisible by 4, except for years divisible by 100 and not by 400.
     * 367 * mon / 12:the calculation is rounded to give the sum of the days of the month.
     * Normal years have 365 days.
     * 719499:Subtract the days before 1970, and the number of days
     * to correct the impact of the leap month
     */
    day = year / 4 - year / 100 + year / 400 + 367 * mon / 12 +
          year * 365 + day - 719499;

    /* 24 hours a day, 60 minutes an hour */
    return ((day * 24 + Time.Hour) * 60 + Time.Minute) * 60 + Time.Second;
}

unsigned long get_jiffies()
{
    EFI_TIME Time = {0};
    EFI_STATUS Status;

    Status = gRT->GetTime(&Time, NULL);
    if (EFI_ERROR(Status)) {
        DebugPrint(DEBUG_ERROR, "get_jiffies GetTime not SUCCESS, returns %r\n",
                   Status);
    }

    return mktime(Time) * SECOND_LEVEL_CONVERSION;
}

unsigned long jiffies_to_msecs(unsigned long n)
{
    return n;
}

unsigned long msecs_to_jiffies(unsigned long m)
{
    return m;
}

/**
 * do_gettimeofday - Returns the time of day in a timeval
 * @tv: pointer to the timeval to be set
 *
 * NOTE: Users should be converted to using getnstimeofday()
 */
void do_gettimeofday(struct timeval *tv)
{
    EFI_TIME Time = {0};
    EFI_STATUS Status;

    Status = gRT->GetTime(&Time, NULL);
    if (EFI_ERROR(Status)) {
        DebugPrint(DEBUG_ERROR, "get_jiffies GetTime not SUCCESS, returns %r\n",
                   Status);
    }

    tv->tv_sec = Time.Second;
    tv->tv_usec = Time.Nanosecond / SECOND_LEVEL_CONVERSION;
}

static UINT8 uefi_rtc_days_in_month[] = {
    31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};

static unsigned long uefi_div_s64_rem(INT64 dividend, int divisor,
                                      int *remainder)
{
    *remainder = dividend % divisor;
    return dividend / divisor;
}

static BOOLEAN uefi_is_leap_year(unsigned int year)
{
    /* leapyears:Divisible by 4, except for years divisible by 100 and not by 400. */
    return (!(year % 4) && (year % 100)) || !(year % 400);
}

/*lint -e514*/
static int uefi_rtc_month_days(unsigned int month, unsigned int year)
{
    return uefi_rtc_days_in_month[month] +
            (uefi_is_leap_year(year) && month == 1);
} /*lint +e514*/

#define TM_START_YEAR       1970
#define DAYS_OF_NORMAL_YEAR 365
#define DAYS_OF_WEEK        7
#define SECONDS_OF_HOUR     3600
#define SECONDS_OF_MIN      60

void rtc_time_to_tm(unsigned long time, struct rtc_time *tm)
{
    int month, year, secs;
    int days;

    /* There are 86400 seconds in a day. */
    days = (int)uefi_div_s64_rem((INT64)time, 86400, &secs);
    /* days since Sunday:0, 1,..., 4, 5, 6 */
    tm->tm_wday = (days + 4) % DAYS_OF_WEEK;
    year = TM_START_YEAR + days / DAYS_OF_NORMAL_YEAR;
    days -= (((year - TM_START_YEAR) * DAYS_OF_NORMAL_YEAR) +
             UEFI_LEAPS_THRU_END_OF(year - 1) -
             UEFI_LEAPS_THRU_END_OF(TM_START_YEAR - 1));
    while (days < 0) {
        year -= 1;
        days += DAYS_OF_NORMAL_YEAR + uefi_is_leap_year(year);
    }

    tm->tm_year = year - TM_START_YEAR;
    tm->tm_yday = days + 1;
    /* month:0, 1, 2,..., 10, 11 */
    for (month = 0; month < 11; month++) {
        int newdays;
        newdays = days - uefi_rtc_month_days(month, year);
        if (newdays < 0) {
            break;
        }
        days = newdays;
    }

    tm->tm_mon = month;
    tm->tm_mday = days + 1;
    tm->tm_hour = secs / SECONDS_OF_HOUR;
    secs -= tm->tm_hour * SECONDS_OF_HOUR;
    tm->tm_min = secs / SECONDS_OF_MIN;
    tm->tm_sec = secs - tm->tm_min * SECONDS_OF_MIN;
}

VOID *dma_zalloc_coherent(VOID *handle, size_t size, dma_addr_t *DeviceAddress,
                          ulong flag)
{
    EFI_PCI_IO_PROTOCOL *PciIo = handle;
    EFI_STATUS Status;
    VOID *HostAddress = NULL;

    if (handle == NULL || DeviceAddress == NULL) {
        return NULL;
    }

    /* Allocate memory for transmit and receive resources. */
    Status = PciIo->AllocateBuffer(PciIo, AllocateAnyPages, EfiBootServicesData,
                                   EFI_SIZE_TO_PAGES(size),
                                   (VOID **)&HostAddress, 0);
    if (EFI_ERROR(Status)) {
        DebugPrint(0x2, "HinicDxe: Failed to allocate DMA buffer, err(%d)\n",
                   Status);
        return NULL;
    }

    /* *DeviceAddress = virt_to_phys(PciIo, HostAddress, size); */
    *DeviceAddress = (UINT64)HostAddress;

    if (*DeviceAddress == 0) {
        return NULL;
    }

    ZeroMem(HostAddress, size);
    MemoryFence();

    return HostAddress;
}

VOID dma_free_coherent(VOID *handle, size_t size, VOID *cpu_addr,
                       dma_addr_t dma_handle)
{
    EFI_PCI_IO_PROTOCOL *PciIo = handle;
    EFI_STATUS Status;

    if (handle == NULL || cpu_addr == NULL) {
        return;
    }

    /* phys_unmap(PciIo, dma_handle); */
    /* Free memory allocated for transmit and receive resources. */
    Status = PciIo->FreeBuffer(PciIo, EFI_SIZE_TO_PAGES(size),
                               (VOID *)cpu_addr);
    if (EFI_ERROR(Status)) {
        DebugPrint(0x2, "HinicDxe: Failed to free DMA buffer\n");
        return;
    }
}

static void *kmalloc_array(size_t n, size_t size, gfp_t flags)
{
	if (size != 0 && n > SIZE_MAX / size)
		return NULL;
	if (__builtin_constant_p(n) && __builtin_constant_p(size))
		return kmalloc(n * size, flags);
	return (void *)NULL;
}

unsigned long *bitmap_zalloc(unsigned int nbits, gfp_t flags)
{
	return kmalloc_array(BITS_TO_LONGS(nbits), sizeof(unsigned long), flags);
}

VOID msleep(UINT32 mstime)
{
    /* gBS->Stall delays microseconds */
    gBS->Stall(mstime * SECOND_LEVEL_CONVERSION);
}

VOID usleep(UINT32 ustime)
{
    gBS->Stall(ustime);
}

VOID usleep_range(UINT32 mix, UINT32 max)
{
    gBS->Stall(max);
}

/**
 * spin_lock_init - create and initialize the spinlock
 * @spinlock_info: the spinlock to  be initialized
 *
 * Create and Initialize the spinlock.
 */
VOID spin_lock_init(spinlock_t *lock)
{
    EfiInitializeLock(lock, HINIC3_LOCK_TPL_LEVEL);
}

/**
 * spin_lock_deinit - delete the spinlock
 * @spinlock_info: the spinlock to  be deleted
 *
 * Delete the spinlock.
 */
VOID spin_lock_deinit(spinlock_t *lock)
{
    /* nothing is done */
}

/**
 * spin_lock_bh - acquire the spinlock
 * @spinlock_info: the spinlock to  be acquired
 * @locktype: the spinlock type to be acquired
 *
 * Acquire the spinlock.
 */
VOID spin_lock_bh(spinlock_t *lock)
{
    if (lock->Lock != EfiLockUninitialized) {
        EfiAcquireLockOrFail(lock);
    }
}

/**
 * spin_unlock_bh - release the spinlock
 * @spinlock_info: the spinlock to  be released
 * @locktype: the spinlock type to be released
 *
 * Release the spinlock.
 */
VOID spin_unlock_bh(spinlock_t *lock)
{
    if (lock->Lock == EfiLockAcquired) {
        EfiReleaseLock(lock);
    }
}

/**
 * sema_init - initialize the semaphore
 * @semaphore_info: the semaphore to ito be initialized
 * @value: initialize value,value=1 means only a thread can get this semaphore
 *
 * Initialize the semaphore.
 */
void sema_init(struct semaphore *sem, int val)
{
    EfiInitializeLock(&sem->sem, HINIC3_LOCK_TPL_LEVEL);
}

/**
 * down - acquire the semaphore
 * @semaphore_info: the semaphore to be acquired
 */
void down(struct semaphore *sem)
{
    if (sem->sem.Lock != EfiLockUninitialized) {
        EfiAcquireLockOrFail(&sem->sem);
    }
}

/**
 * up - release the semaphore
 * @semaphore_info: the semaphore to release
 *
 * Release the semaphore.
 */
void up(struct semaphore *sem)
{
    if (sem->sem.Lock == EfiLockAcquired) {
        EfiReleaseLock(&sem->sem);
    }
}

void init_completion(struct completion *x)
{
    EFI_TPL OldTpl;

    OldTpl = gBS->RaiseTPL(HINIC3_LOCK_TPL_LEVEL);
    x->done = 0;
    gBS->RestoreTPL(OldTpl);

    MemoryFence();
    return;
}

BOOLEAN wait_for_completion_timeout(struct completion *x, ulong timeout)
{
    return TRUE;
}

void wait_for_completion(struct completion *x)
{
    ulong start = 0;
    ulong end = jiffies_to_msecs(LONG_MAX);

    if (x == NULL) {
        return;
    }

    while (start < end) {
        if (x->done != 0)
            return;
        msleep(1);
        start++;
    }
}

void complete(struct completion *x)
{
    EFI_TPL OldTpl;

    OldTpl = gBS->RaiseTPL(TPL_HIGH_LEVEL);
    x->done++;
    gBS->RestoreTPL(OldTpl);

    MemoryFence();
}

void mutex_init(struct mutex *lock)
{
    EfiInitializeLock(&lock->mutex_lock, HINIC3_LOCK_TPL_LEVEL);
}

int mutex_trylock(struct mutex *lock)
{
    EfiAcquireLockOrFail(&lock->mutex_lock);
    return 1;
}

void mutex_lock(struct mutex *lock)
{
    EfiAcquireLockOrFail(&lock->mutex_lock);
}

void mutex_unlock(struct mutex *lock)
{
    EfiReleaseLock(&lock->mutex_lock);
}

void *io_mapping_map_wc(void *ignored1, UINT64 ignored2)
{
    return (void *)1;
}

void io_mapping_unmap(void *ignored) {}

#define SHIFT_56 56
#define SHIFT_40 40
#define SHIFT_24 24
#define SHIFT_8  8

UINT64 cpu_to_be64(UINT64 x)
{
    return ((((x) & 0x00000000000000ff) << SHIFT_56) |
            (((x) & 0x000000000000ff00) << SHIFT_40) |
            (((x) & 0x0000000000ff0000) << SHIFT_24) |
            (((x) & 0x00000000ff000000) << SHIFT_8) |
            (((x) & 0xff00000000000000) >> SHIFT_56) |
            (((x) & 0x00ff000000000000) >> SHIFT_40) |
            (((x) & 0x0000ff0000000000) >> SHIFT_24) |
            (((x) & 0x000000ff00000000) >> SHIFT_8));
}

UINT32 cpu_to_be32(UINT32 x)
{
    return ((((x) & 0x000000ff) << SHIFT_24) | (((x) & 0x0000ff00) << SHIFT_8) |
            (((x) & 0x00ff0000) >> SHIFT_8) | (((x) & 0xff000000) >> SHIFT_24));
}

UINT16 cpu_to_be16(UINT16 x)
{
    return ((((x) & 0x00ff) << SHIFT_8) | (((x) & 0xff00) >> SHIFT_8));
}

UINT64 be64_to_cpu(UINT64 x)
{
    return cpu_to_be64(x);
}

UINT32 be32_to_cpu(UINT32 x)
{
    return cpu_to_be32(x);
}

UINT16 be16_to_cpu(UINT16 x)
{
    return cpu_to_be16(x);
}

struct dma_pool *dma_pool_create(char *name, void *dev_hdl,
                                 size_t size, size_t align,
                                 size_t allocation)
{
    struct dma_pool *pool = NULL;

    pool = (struct dma_pool *)vzalloc(sizeof(struct dma_pool));
    if (pool == NULL) {
        return NULL;
    }

    pool->size = size;
    pool->dev_hdl = dev_hdl;

    return pool;
}

void dma_pool_destroy(struct dma_pool *pool)
{
    pool->dev_hdl = NULL;
    vfree(pool);
}

/**
 * atomic_read - read atomic variable
 * @v: pointer of type atomic_t
 */
int atomic_read(atomic_t *v)
{
    EFI_TPL OldTpl;
    int r;

    OldTpl = gBS->RaiseTPL(TPL_HIGH_LEVEL);
    r = (int)v->counter;
    gBS->RestoreTPL(OldTpl);

    return r;
}

void atomic_set(atomic_t *addr, int newval)
{
    EFI_TPL OldTpl;

    OldTpl = gBS->RaiseTPL(TPL_HIGH_LEVEL);
    addr->counter = newval;
    gBS->RestoreTPL(OldTpl);
}

void atomic_add(int i, atomic_t *v)
{
    EFI_TPL OldTpl;

    OldTpl = gBS->RaiseTPL(TPL_HIGH_LEVEL);
    v->counter = v->counter + i;
    gBS->RestoreTPL(OldTpl);
}

void atomic_inc(atomic_t *v)
{
    EFI_TPL OldTpl;

    OldTpl = gBS->RaiseTPL(TPL_HIGH_LEVEL);
    v->counter = v->counter + 1;
    gBS->RestoreTPL(OldTpl);
}

void atomic_dec(atomic_t *v)
{
    EFI_TPL OldTpl;

    OldTpl = gBS->RaiseTPL(TPL_HIGH_LEVEL);
    v->counter = v->counter - 1;
    gBS->RestoreTPL(OldTpl);
}

/**
 * atomic_add_return - add integer and return
 * @i: integer value to add
 * @v: pointer of type atomic_t
 *
 * Atomically adds @i to @v and returns @i + @v
 */
int atomic_add_return(int i, atomic_t *v)
{
    EFI_TPL OldTpl;
    int tmp;

    OldTpl = gBS->RaiseTPL(TPL_HIGH_LEVEL);
    v->counter = v->counter + i;
    tmp = v->counter;
    gBS->RestoreTPL(OldTpl);

    return tmp;
}

/**
 * atomic_sub_return - subtract integer and return
 * @v: pointer of type atomic_t
 * @i: integer value to subtract
 *
 * Atomically subtracts @i from @v and returns @v - @i
 */
int atomic_sub_return(int i, atomic_t *v)
{
    return atomic_add_return(-i, v);
}

void *memcpy(void *s1, const void *s2, size_t n)
{
    return (void *)MemCpyS(s1, n, s2, n);
}

void *memset(void *s, int c, size_t n)
{
    return (void *)MemSetS(s, n, (UINT8)c, (UINTN)n);
}

int memcpy_s(void *s1, size_t destMax, const void *s2, size_t n)
{
    return MemCpyS(s1, destMax, s2, n);
}

int memset_s(void *s, size_t destMax, int c, size_t n)
{
    return MemSetS(s, destMax, (UINT8)c, (UINTN)n);
}

int strncpy_s(char *strDest, size_t destMax, const char *strSrc, size_t count)
{
    return StrnCpyS((CHAR16 *)strDest, destMax, (CHAR16 *)strSrc, count);
}

#define small_const_nbits(nbits) \
        ((nbits) <= (sizeof(unsigned long) * BITS_PER_BYTE))

void bitmap_copy(unsigned long *dst, const unsigned long *src, int nbits)
{
    if (small_const_nbits(nbits)) {
        *dst = *src;
    } else {
        int len = BITS_TO_LONGS(nbits) * sizeof(unsigned long);
        memcpy_s(dst, len, src, len);
    }
}

void _set_bit(long nr, volatile unsigned long *addr)
{
    EFI_TPL OldTpl;

    OldTpl = gBS->RaiseTPL(TPL_HIGH_LEVEL);
    *addr |= 1 << nr;
    gBS->RestoreTPL(OldTpl);
}

void _clear_bit(long nr, volatile unsigned long *addr)
{
    EFI_TPL OldTpl;

    OldTpl = gBS->RaiseTPL(TPL_HIGH_LEVEL);
    *addr &= (~(1 << nr));
    gBS->RestoreTPL(OldTpl);
}

int _test_bit(long nr, volatile unsigned long *addr)
{
    EFI_TPL OldTpl;
    int val;

    OldTpl = gBS->RaiseTPL(TPL_HIGH_LEVEL);
    val = !!((*addr) & (1 << nr));
    gBS->RestoreTPL(OldTpl);

    return val;
}

int find_first_zero_bit(const unsigned long *vaddr, unsigned size)
{
    const unsigned long *p = vaddr;
    int res = 0;
    unsigned int words;
    unsigned int word;
    unsigned long num;
    int i;

    if (!size)
        return 0;

    words = (size + INT_BIT_WIDTH - 1) >> INT_BIT_SHIFT;
    word = words;
    while (!(num = ~*p++)) {
        if (!--words)
            goto out;
    }

    for (i = 0; i < size; i++) {
        if ((num & 1)== 1)
            break;
        num = num >> 1;
    }
    res = i;
out:
    res += (word - words) * INT_BIT_WIDTH;
    return res < size ? res : size;
}

UINT16 pci_enable_msix_range(void *dev, struct msix_entry *entries, int minvec,
                             UINT16 maxvec)
{
    INT32 i;
    UINT16 cnt = maxvec;

    for (i = 0; i < cnt; i++) {
        entries[i].entry = (UINT16)i;
        entries[i].vector = i;
    }

    return cnt;
}

void synchronize_irq(UINT32 irq)
{
    return;
}

void cancel_work_sync(struct work_struct *work)
{
    return;
}

struct workqueue_struct *create_singlethread_workqueue(char *name)
{
    return vzalloc(sizeof(struct workqueue_struct));
}

struct workqueue_struct *alloc_workqueue(const char *fmt, unsigned int flags,
                                         int max_active, ...)
{
    return vzalloc(sizeof(struct workqueue_struct));
}

void destroy_workqueue(struct workqueue_struct *wq)
{
    vfree((void *)wq);
}

void destroy_work(struct work_struct *work)
{
    return;
}

int queue_work(struct workqueue_struct *wq, struct work_struct *work)
{
    if (wq == NULL || work == NULL) {
        return 0;
    }

    if (work->foo) {
        work->foo(work);
    }

    return 1;
}

void INIT_WORK(struct work_struct *work,
               void (*eq_irq_work)(struct work_struct *work))
{
    return;
}

UINT8 work_busy(struct work_struct *work)
{
#define WORK_BUSY_RUNNING 1 << 1
    return WORK_BUSY_RUNNING;
}

INT32 request_irq(UINT32 irq, irq_handler_t handler, ulong flags,
                  const CHAR8 *name, VOID *dev)
{
    return 0;
}

void free_irq(UINT32 irq, void *dev)
{
    return;
}

void tasklet_init(struct tasklet_struct *t, void (*func)(ulong), ulong data)
{
    if (t == NULL) {
        return;
    }

    t->bEnable = 1;
    t->fun = func;
    t->data = (void *)data;
    return;
}


void initialize_timer(void *adapter_hdl, struct timer_list *timer)
{
	return;
}

void add_to_timer(struct timer_list *timer, LONG period)
{
	return;
}

void stop_timer(struct timer_list *timer)
{
	return;
}

void delete_timer(struct timer_list *timer)
{
	return;
}

void mod_timer(struct timer_list *timer, u64 expires)
{
	return;
}

#define ETHER_ADDR_LEN 6
void ether_addr_copy(UINT8 *dst, const UINT8 *src)
{
    memcpy_s(dst, ETHER_ADDR_LEN, src, ETHER_ADDR_LEN);
}

UINT32 readl_uefi(EFI_PCI_IO_PROTOCOL *PciIO, UINT32 reg, UINT8 bar_idx)
{
    UINT32 Results;
    EFI_STATUS Status;

    if (PciIO == NULL) {
        DebugPrint(DEBUG_ERROR, "Read PciIo == NULL\n");
        return (UINT32)(-1);
    }

    MemoryFence();
    Status = PciIO->Mem.Read(PciIO, EfiPciIoWidthUint32, bar_idx, reg, 1,
                             (VOID *)(&Results));
    MemoryFence();

    if (EFI_ERROR(Status)) {
        DebugPrint(DEBUG_ERROR,
                   "Hinic3: PciIo->Mem.Read() fails: %r\n", Status);
        return (UINT32)(-1);
    }

    return Results;
}

void writel_uefi(EFI_PCI_IO_PROTOCOL *PciIO, UINT32 reg, UINT8 bar_idx,
                 UINT32 val)
{
    EFI_STATUS Status;

    if (PciIO == NULL) {
        DebugPrint(DEBUG_ERROR, "write_reg() PciIo == NULL\n");
        return;
    }

    MemoryFence();
    Status = PciIO->Mem.Write(PciIO, EfiPciIoWidthUint32, bar_idx, reg, 1,
                              (void *)(&val));
    MemoryFence();

    if (EFI_ERROR(Status)) {
        DebugPrint(DEBUG_ERROR,
                   "Hinic3: PciIo->Mem.Write() fails: %r\n", Status);
    }

    return;
}

void writeq_uefi(EFI_PCI_IO_PROTOCOL *PciIO, UINT64 reg, UINT8 bar_idx,
                 UINT64 val)
{
    EFI_STATUS Status;

    if (PciIO == NULL) {
        DebugPrint(DEBUG_ERROR, "write_reg() PciIo == NULL\n");
        return;
    }

    MemoryFence();
    Status = PciIO->Mem.Write(PciIO, EfiPciIoWidthUint64, bar_idx, reg, 1,
                              (void *)(&val));
    MemoryFence();

    if (EFI_ERROR(Status)) {
        DebugPrint(DEBUG_ERROR,
                   "Hinic3: PciIo->Mem.Write() fails: %r\n", Status);
    }

    return;
}
